<template>
	<div ref="wrapperRef" :class="getWrapperClass">
		<BasicForm
			ref="formRef"
			:submitOnReset="false"
			v-bind="getFormProps"
			v-if="getBindValues.useSearchForm"
			:tableAction="tableAction"
			@register="registerForm"
			@submit="handleSearchInfoChange"
		>
			<template #[replaceFormSlotKey(item)]="data" v-for="item in getFormSlotKeys">
				<slot :name="item" v-bind="data || {}"></slot>
			</template>
		</BasicForm>
		<slot name="beforeTable"></slot>
		<Table ref="tableElRef" v-bind="getBindValues" @change="handleTableChange">
			<template #[item]="data" v-for="item in Object.keys($slots)" :key="item">
				<slot :name="item" v-bind="data || {}"></slot>
			</template>
			<template #headerCell="{ column }">
				<HeaderCell :column="column" />
			</template>
			<template #bodyCell="data">
				<slot name="bodyCell" v-bind="data || {}"></slot>
			</template>
		</Table>
	</div>
</template>
<script lang="ts">
import { computed, defineComponent, inject, ref, toRaw, unref, watchEffect } from 'vue';
import { Table } from 'ant-design-vue';
import { BasicForm, useForm } from '#/Form';
import HeaderCell from './components/HeaderCell.vue';
import { basicProps } from './props';
import { useTableHeader } from './hooks/useTableHeader';
import { useLoading } from './hooks/useLoading';
import { usePagination } from './hooks/usePagination';
import { useColumns } from './hooks/useColumns';
import { useDataSource } from './hooks/useDataSource';
import { useTableForm } from './hooks/useTableForm';
import { BasicTableProps } from './types/table';
import { TableActionType } from './types/tableAction';
import { useDesign } from '@/hooks/web/useDesign';
import { PageWrapperFixedHeightKey } from '#/Page';
import { useRowKey } from './hooks/useRowKey';
import { useRowSelection } from './hooks/useRowSelection';
import { isFunction } from '@/utils/is';
import { useTableScroll } from './hooks/useTableScroll';
import { useTableScrollTo } from './hooks/useScrollTo';
import { useTableExpand } from './hooks/useTableExpand';
import { omit } from 'lodash-es';
import { useTableStyle } from './hooks/useTableStyle';
import { createTableContext } from './hooks/useTableContext';

export default defineComponent({
	name: 'BasicTable',
	components: { Table, BasicForm, HeaderCell },
	props: basicProps,
	emits: [
		'register',
		'fetch-success',
		'fetch-error',
		'selection-change',
		'change',
		'expanded-rows-change',
		'edit-end',
		'edit-cancel',
		'edit-row-end',
		'edit-change',
	],
	setup(props, { attrs, emit, slots }) {
		const wrapperRef = ref(null);
		const formRef = ref(null);
		const tableElRef = ref(null);

		const tableData = ref<Recordable[]>([]);

		const filteredInfo = ref<Recordable>();
		const sorteredInfo = ref();

		// 接收useTable传来的props
		const receivedPropsRef = ref<Partial<BasicTableProps>>();
		const [registerForm, formActions] = useForm();

		const { prefixCls } = useDesign('basic-table');

		// 获取所有的props：useTable+组件传递
		const getProps = computed(() => {
			return { ...props, ...unref(receivedPropsRef) } as BasicTableProps;
		});

		const isFixedHeightPage = inject(PageWrapperFixedHeightKey, false);

		watchEffect(() => {
			unref(isFixedHeightPage) &&
				props.canResize &&
				console.warn("'canResize' 和 PageWrapper 中的 'fixedHeight'共存时可能出现高度计算错误");
		});

		// tableTitle
		let { getHeaderProps } = useTableHeader(getProps, slots);

		const { setLoading, getLoading } = useLoading(getProps);

		const { getAutoCreateKey, getRowKey } = useRowKey(getProps);

		const { getPagination, setPagination, getPaginationRef, getShowPagination, setShowPagination } =
			usePagination(getProps);

		const {
			getRowSelection,
			getRowSelectionRef,
			getSelectRows,
			setSelectedRows,
			clearSelectedRowKeys,
			getSelectRowKeys,
			deleteSelectRowByKey,
			setSelectedRowKeys,
		} = useRowSelection(getProps, tableData, { getRowKey }, emit);

		// columns
		const { getColumnsRef, getViewColumns, getColumns } = useColumns(
			getProps,
			getPaginationRef,
			filteredInfo,
			sorteredInfo,
		);

		const {
			getDataSourceRef,
			getDataSource,
			setDataSource,
			getRawDataSource,
			reload,
			fetch,
			handleTableChange: onTableChange,
			updateTableData,
			updateTableDataRecord,
		} = useDataSource(
			getProps,
			{
				tableData,
				setLoading,
				getPaginationRef,
				setPagination,
				getFieldsValue: formActions.getFieldsValue,
				clearSelectedRowKeys,
				getAutoCreateKey,
				getRowKey,
			},
			emit,
			filteredInfo,
		);

		function handleTableChange(...args) {
			onTableChange.call(undefined, ...args);
			emit('change', ...args);
			// 解决通过useTable注册onChange时不起作用的问题
			const { onChange } = unref(getProps);
			onChange && isFunction(onChange) && onChange.call(undefined, ...args);
		}

		const { getScrollRef, redoHeight } = useTableScroll(
			getProps,
			tableElRef,
			getColumnsRef,
			getRowSelectionRef,
			getDataSourceRef,
			wrapperRef,
			formRef,
			isFixedHeightPage,
		);

		const { scrollTo } = useTableScrollTo(tableElRef, getDataSourceRef);

		const { getRowClassName } = useTableStyle(getProps, prefixCls);

		const { getExpandOption, expandAll, expandRows, collapseAll } = useTableExpand(getProps, tableData, emit);

		const { getFormProps, replaceFormSlotKey, getFormSlotKeys, handleSearchInfoChange } = useTableForm(
			getProps,
			slots,
			fetch,
			getLoading,
			filteredInfo,
		);

		const getBindValues = computed(() => {
			let propsData = {
				...attrs,
				...unref(getProps),
				...unref(getHeaderProps),
				scroll: unref(getScrollRef),
				loading: unref(getLoading),
				tableLayout: 'fixed',
				rowKey: unref(getRowKey),
				rowSelection: unref(getRowSelectionRef),
				columns: toRaw(unref(getViewColumns)),
				dataSource: unref(getDataSourceRef),
				pagination: toRaw(unref(getPaginationRef)),
				...unref(getExpandOption),
			} as Recordable;
			if (slots.expandedRowRender) {
				propsData = omit(propsData, 'scroll');
			}
			propsData = omit(propsData, ['class', 'onChange']);
			return propsData;
		});

		const getWrapperClass = computed(() => {
			const values = unref(getBindValues);
			return [
				prefixCls,
				attrs.class,
				{
					[`${prefixCls}-form-container`]: values.useSearchForm,
					[`${prefixCls}--inset`]: values.inset,
				},
			];
		});

		function setProps(props?: Partial<BasicTableProps>) {
			receivedPropsRef.value = props;
		}

		const tableAction: TableActionType = {
			reload,
			getSelectRows,
			clearSelectedRowKeys,
			getSelectRowKeys,
			setSelectedRows,
			deleteSelectRowByKey,
			setPagination,
			setDataSource,
			updateTableDataRecord,
			redoHeight,
			setSelectedRowKeys,
			setLoading,
			getDataSource,
			getRawDataSource,
			setProps,
			getRowSelection,
			getPaginationRef: getPagination,
			getColumns,
			emit,
			updateTableData,
			setShowPagination,
			getShowPagination,
			expandAll,
			expandRows,
			collapseAll,
			scrollTo,
		};

		createTableContext({ ...tableAction, wrapperRef, getBindValues });

		emit('register', tableAction, formActions);

		return {
			wrapperRef,
			formRef,
			tableElRef,
			getWrapperClass,
			prefixCls,
			registerForm,
			getBindValues,
			handleTableChange,
			getFormProps: getFormProps as any,
			replaceFormSlotKey,
			getFormSlotKeys,
			getRowClassName,
			handleSearchInfoChange,
			tableAction,
			redoHeight,
			columns: getViewColumns,
		};
	},
});
</script>
<style lang="less">
@prefix-cls: ~'@{namespace}-basic-table';
.@{prefix-cls} {
	&-before-table {
		margin: 15px 0;
	}
}
</style>
